package com.example.synchost5.service.local;

import com.example.synchost5.exception.ControllableException;
import com.example.synchost5.job.FetchInfoJob;
import com.example.synchost5.service.SyncHostService;
import com.example.synchost5.store.DataStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Component
public class SyncHostServiceImpl implements SyncHostService {

    @Autowired
    private DataStore dataStore;

    @Autowired
    private FetchInfoJob fetchInfoJob;

    // 存疑，来源：https://www.cnblogs.com/sunliyuan/p/14378518.html
    private static final String ipRegex = "(((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$|^([\\da-fA-F]{1,4}:){6}((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$|^::([\\da-fA-F]{1,4}:){0,4}((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$|^([\\da-fA-F]{1,4}:):([\\da-fA-F]{1,4}:){0,3}((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$|^([\\da-fA-F]{1,4}:){2}:([\\da-fA-F]{1,4}:){0,2}((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$|^([\\da-fA-F]{1,4}:){3}:([\\da-fA-F]{1,4}:){0,1}((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$|^([\\da-fA-F]{1,4}:){4}:((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$|^([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$|^:((:[\\da-fA-F]{1,4}){1,6}|:)$|^[\\da-fA-F]{1,4}:((:[\\da-fA-F]{1,4}){1,5}|:)$|^([\\da-fA-F]{1,4}:){2}((:[\\da-fA-F]{1,4}){1,4}|:)$|^([\\da-fA-F]{1,4}:){3}((:[\\da-fA-F]{1,4}){1,3}|:)$|^([\\da-fA-F]{1,4}:){4}((:[\\da-fA-F]{1,4}){1,2}|:)$|^([\\da-fA-F]{1,4}:){5}:([\\da-fA-F]{1,4})?$|^([\\da-fA-F]{1,4}:){6}:)";

    private static final Pattern pattern = Pattern.compile(ipRegex);

    @Override
    public boolean saveConfig(String configName, String staticPart, String dynamicPart) {
        if (dataStore.hasConfigName(configName)) {
            throw new ControllableException("名称已存在");
        }
        boolean saved = dataStore.saveConfig(configName, staticPart, resolveDynamicPart(dynamicPart));
        if (saved) {
            fetchInfoJob.autoFetch();
        }
        return saved;
    }

    @Deprecated
    @Override
    public Boolean checkNameExists(String configName) {
        return null;
    }

    @Override
    public String getFullHostFile(String configName) {
        String staticPart = dataStore.getStaticPart(configName);
        Set<String> dynamicPart = dataStore.getDynamicPart(configName);
        Map<String, String> ipMap = new LinkedHashMap<>();
        if (dynamicPart != null && !dynamicPart.isEmpty()) {
            List<String> sortedDomain = dynamicPart.stream().sorted().collect(Collectors.toList());
            for (String domain : sortedDomain) {
                ipMap.put(domain, dataStore.getIP(domain));
            }
        }
        return generateHostFile(configName, staticPart, ipMap);
    }

    @Override
    public void fetch() {
        fetchInfoJob.autoFetch();
    }

    /**
     * 提取 host 的 domain
     * @param dynamicPart 新版接口此参数为 host 文件的一部分
     * @return 提取好的域名
     */
    private Set<String> resolveDynamicPart(String dynamicPart) {
        Set<String> domains = new HashSet<>();
        String[] splits = dynamicPart.split("\\t| |(\\r?\\n)");
        for (String split : splits) {
            if (pattern.matcher(split).find() || !split.contains(".")) {
                continue;
            }
            domains.add(split);
        }
        if (domains.isEmpty()) {
            domains.add("www.github.com");
        }
        return new HashSet<>(domains);
    }

    private String generateHostFile(String configName, String staticPart, Map<String, String> ips) {
        StringBuilder sb = new StringBuilder("# SyncHost start - ").append(configName).append("\n");
        if (staticPart != null && !staticPart.isEmpty()) {
            sb.append(staticPart).append("\n#-------------------\n");
        }
        if (!ips.isEmpty()) {
            for (Map.Entry<String, String> entry : ips.entrySet()) {
                sb.append(entry.getValue()).append("\t").append(entry.getKey()).append("\n");
            }
        }
        sb.append("# SyncHost end - ").append(new Date()).append("\n");
        return sb.toString();
    }
}
